package org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.http.socket;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.client.config.NettyClientProperties;
import org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.http.NettyHttpClientProxyServer;
import org.framework.lazy.cloud.network.heartbeat.client.netty.proxy.http.filter.NettyHttpClientProxyServerProxyFilter;
import org.framework.lazy.cloud.network.heartbeat.common.advanced.payload.NettyProxyMsg;
import org.framework.lazy.cloud.network.heartbeat.common.adapter.ChannelTypeAdapter;
import org.framework.lazy.cloud.network.heartbeat.common.constant.ProxyMessageType;
import org.framework.lazy.cloud.network.heartbeat.common.utils.ChannelAttributeKeyUtils;

import java.util.concurrent.TimeUnit;

/**
 * 客户端代理客户端传输通道
 */
@Slf4j
public class NettyHttpClientProxyServerProxySocket {
    static EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

    /**
     * 连接服务端通信通道
     * @param nettyHttpClientProxyServer 客户端代理服务端配置信息
     * @param originChannel  原始通道
     */
    public static void buildTransferServer(NettyHttpClientProxyServer nettyHttpClientProxyServer, Channel originChannel) {


        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.SO_KEEPALIVE, true)
                // 设置读缓冲区为2M
                .option(ChannelOption.SO_RCVBUF, 2048 * 1024)
                // 设置写缓冲区为1M
                .option(ChannelOption.SO_SNDBUF, 1024 * 1024)
//                .option(ChannelOption.TCP_NODELAY, false)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000 * 60)//连接超时时间设置为 60 秒
//                .option(ChannelOption.SO_BACKLOG, 256)//务端接受连接的队列长度 默认128
//                .option(ChannelOption.RCVBUF_ALLOCATOR, new NettyRecvByteBufAllocator(1024 * 1024))//用于Channel分配接受Buffer的分配器 默认AdaptiveRecvByteBufAllocator.DEFAULT

                .option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024 * 1024, 1024 * 1024 * 2))
                .handler(new NettyHttpClientProxyServerProxyFilter(new ChannelTypeAdapter(nettyHttpClientProxyServer.getHandleChannelTypeAdvancedList())))
        ;
        NettyClientProperties nettyClientProperties = nettyHttpClientProxyServer.getNettyClientProperties();
        String inetHost = nettyClientProperties.getInetHost();
        int inetPort = nettyClientProperties.getInetPort();
        // local client id

        String clientId = nettyClientProperties.getClientId();

        String targetIp = nettyHttpClientProxyServer.getTargetIp();
        Integer targetPort = nettyHttpClientProxyServer.getTargetPort();
        byte[] data = nettyHttpClientProxyServer.getData();

        String visitorId = ChannelAttributeKeyUtils.getVisitorId(originChannel);



        // 客户端新建访客通道 连接服务端IP:{},连接服务端端口:{}
        log.info("Client creates a new visitor channel to connect to server IP: {}, connecting to server port: {} with clientId:【{}】 toClientId:【{}】 & visitorId:【{}】", inetHost, inetPort, clientId, clientId, visitorId);
        ChannelFuture future = bootstrap.connect(inetHost, inetPort);

        // 使用的客户端ID:{}
        future.addListener((ChannelFutureListener) futureListener -> {
            Channel transferChannel = futureListener.channel();
            if (futureListener.isSuccess()) {

                NettyProxyMsg nettyProxyMsg = new NettyProxyMsg();
                nettyProxyMsg.setType(ProxyMessageType.HTTP_REPORT_CLIENT_PROXY_SERVER_TRANSFER_);
                // other clientId
                nettyProxyMsg.setClientId(clientId);

                nettyProxyMsg.setClientTargetIp(targetIp);
                nettyProxyMsg.setClientTargetPort(targetPort);
                nettyProxyMsg.setData(data);

                nettyProxyMsg.setVisitorId(visitorId);
                transferChannel.writeAndFlush(nettyProxyMsg);
                // 绑定客户端真实通信通道
                ChannelAttributeKeyUtils.buildVisitorId(transferChannel, visitorId);
                ChannelAttributeKeyUtils.buildClientId(transferChannel, clientId);
                // 传输通道打开后自动读取
                ChannelAttributeKeyUtils.buildNextChannel(originChannel, transferChannel);
                ChannelAttributeKeyUtils.buildNextChannel(transferChannel, originChannel);


            } else {
                log.info("无法连接到服务端....");
                eventLoopGroup.schedule(() -> {
                    try {
                        buildTransferServer(nettyHttpClientProxyServer, originChannel);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, 2, TimeUnit.SECONDS);
            }
        });
    }
}